package com.basker.pisces.service.context;

import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.basker.pisces.context.ILoginContextManager;
import com.basker.pisces.context.LoginContext;
import com.basker.pisces.context.LoginContextHolder;
import com.basker.pisces.context.Token;
import com.basker.pisces.service.annotation.IgnoreLoginContext;
import com.basker.pisces.service.constant.ServiceConstants;
import com.basker.pisces.service.exception.LoginContextNotFoundException;

public class LoginContextInterceptor extends HandlerInterceptorAdapter {

    private static final String TOKEN_ID_KEY = ServiceConstants.MessageHead.TOKEN_ID_KEY;

    @Autowired(required = false)
    private ILoginContextManager<?> manager;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        if (this.ignoreLoginContext(handler)) {
            return true;
        }

        LoginContext loginContext = this.tryGetLoginContext(request);
        if (loginContext == null) {
            throw new LoginContextNotFoundException();
        }

        return true;
    }

    private boolean ignoreLoginContext(Object handler) throws Exception {
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();

        if (!AnnotatedElementUtils.hasAnnotation(method.getDeclaringClass(), RestController.class)) {
            return true;
        }

        return AnnotatedElementUtils.hasAnnotation(method, IgnoreLoginContext.class);
    }

    private LoginContext tryGetLoginContext(HttpServletRequest request) {
        if (this.manager == null) {
            return null;
        }

        String tokenId = request.getHeader(TOKEN_ID_KEY);
        if (StringUtils.isEmpty(tokenId)) {
            return null;
        }

        LoginContext loginContext = this.manager.get(new Token(tokenId));
        if (loginContext != null) {
            LoginContextHolder.setLoginContext(loginContext, true);
        }

        return loginContext;
    }
}
